﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Xml;
using System.Xml.Linq;

using TJDevHouse.Starcraft.Game;

namespace TJDevHouse.Starcraft.AIModuleConnector.ImportExport
{
	/// <summary>
	/// Generates map data which can be used for import/export operations
	/// </summary>
	class XMLMapDataImporterExporter
	{
		/// <summary>
		/// Creates the xml for an individual tile
		/// </summary>
		/// <param name="doc"></param>
		/// <param name="x"></param>
		/// <param name="y"></param>
		/// <returns></returns>
		public XmlElement MakeTileXMLElement(XmlDocument doc, int x, int y)
		{
			return MakeTileXMLElement(doc, x, y, "t");
		}


		/// <summary>
		/// Creates the XML for a specific tile.
		/// 
		/// Allows for a custom element name.
		/// </summary>
		/// <param name="doc"></param>
		/// <param name="x"></param>
		/// <param name="y"></param>
		/// <param name="elementName"></param>
		/// <returns></returns>
		public XmlElement MakeTileXMLElement(XmlDocument doc, int x, int y, string elementName)
		{
			XmlElement tile = doc.CreateElement(elementName);

			tile.SetAttribute("x", x.ToString());
			tile.SetAttribute("y", y.ToString());

			return tile;
		}

		/// <summary>
		/// Creates the map's data in exportable format
		/// </summary>
		/// <returns></returns>
		public XDocument MakeExportMapData(Map m)
		{
			XmlDocument exportData = new XmlDocument();
			XmlDeclaration dec = exportData.CreateXmlDeclaration("1.0", null, null);

			XmlElement map = exportData.CreateElement("map");

			XmlElement hash = exportData.CreateElement("hash");
			hash.InnerText = m.MapHash.ToString();

			XmlElement name = exportData.CreateElement("name");
			name.InnerText = m.MapName;

			XmlElement width = exportData.CreateElement("width");
			width.InnerText = m.MapBuildWidth.ToString();

			XmlElement height = exportData.CreateElement("height");
			height.InnerText = m.MapBuildHeight.ToString();

			map.AppendChild(hash);
			map.AppendChild(name);
			map.AppendChild(width);
			map.AppendChild(height);

			XmlElement starts = exportData.CreateElement("starting_positions");
			foreach (StartingLocation sl in m.StartingLocations)
			{
				XmlElement p = MakeTileXMLElement(exportData, sl.X, sl.Y, "position");
				starts.AppendChild(p);
			}
			map.AppendChild(starts);

			XmlElement resources = exportData.CreateElement("resources");
			XmlElement minerals = exportData.CreateElement("minerals");
			foreach (StaticMapResource mTile in m.StaticMinerals)
			{
				XmlElement mineral = MakeTileXMLElement(exportData, mTile.BuildTile.X, mTile.BuildTile.Y, "mineral");
				minerals.AppendChild(mineral);
			}
			resources.AppendChild(minerals);

			XmlElement geysers = exportData.CreateElement("geysers");
			foreach (StaticMapResource gTile in m.StaticVespeneGeysers)
			{
				XmlElement geyser = MakeTileXMLElement(exportData, gTile.BuildTile.X, gTile.BuildTile.Y, "geyser");
				geysers.AppendChild(geyser);
			}
			resources.AppendChild(geysers);

			map.AppendChild(resources);

			XmlElement tiles = exportData.CreateElement("tiles");

			foreach (WalkTile wt in m.WalkTiles)
			{
				XmlElement tile = MakeTileXMLElement(exportData, wt.X, wt.Y);

				XmlElement buildable = exportData.CreateElement("b");
				if (wt.IsBuildable)
				{
					buildable.InnerText = "1";
				}
				else
				{
					buildable.InnerText = "0";
				}
				tile.AppendChild(buildable);

				XmlElement walkable = exportData.CreateElement("w");
				if (wt.IsWalkable)
				{
					walkable.InnerText = "1";
				}
				else
				{
					walkable.InnerText = "0";
				}
				tile.AppendChild(walkable);

				XmlElement tileHeight = exportData.CreateElement("h");
				tileHeight.InnerText = wt.IsHighGround.ToString();
				tile.AppendChild(tileHeight);

				tiles.AppendChild(tile);
			}


			map.AppendChild(tiles);

			exportData.AppendChild(map);

			return XDocument.Load(new XmlNodeReader(exportData));
		}

		/// <summary>
		/// Takes an XElement containing X and Y elements and converts to
		/// a starting location.
		/// </summary>
		/// <param name="element"></param>
		/// <returns></returns>
		public BuildTileMapCoordinate MakeBuildTileMapCoordinateFromXElement(XElement element)
		{
			return new BuildTileMapCoordinate(
					int.Parse(element.Element("x").Value), int.Parse(element.Element("y").Value)
				);
		}

		public Map MakeMapFromMapData(XDocument mapXML)
		{
			int hash, w, h;
			string name;

			List<BuildTileMapCoordinate> startingLocations = new List<BuildTileMapCoordinate>();
			List<BuildTileMapCoordinate> staticMinerals = new List<BuildTileMapCoordinate>();
			List<BuildTileMapCoordinate> staticGas = new List<BuildTileMapCoordinate>();

			bool[][] buildable;
			bool[][] walkable;
			int[][] height;

			var mapData = mapXML.Element("map");

			hash = int.Parse(mapData.Element("hash").Value);
			name = mapData.Element("name").Value;
			w = int.Parse(mapData.Element("width").Value);
			h = int.Parse(mapData.Element("height").Value);

			foreach (var position in mapData.Element("starting_positions").Elements("position"))
			{
				startingLocations.Add(MakeBuildTileMapCoordinateFromXElement(position));
			}

			foreach (var mineral in mapData.Element("resources").Element("minerals").Elements("mineral"))
			{
				staticMinerals.Add(MakeBuildTileMapCoordinateFromXElement(mineral));
			}

			foreach (var geyser in mapData.Element("resources").Element("geysers").Elements("geyser"))
			{
				staticGas.Add(MakeBuildTileMapCoordinateFromXElement(geyser));
			}

			buildable = new bool[h][];
			walkable = new bool[h][];
			height = new int[h][];

			for (int i = 0; i < h; i++)
			{
				buildable[i] = new bool[w];
				walkable[i] = new bool[w];
				height[i] = new int[w];
			}

			foreach (var aTile in mapData.Element("tiles").Elements("tile"))
			{
				int x, y;
				x = int.Parse(aTile.Element("x").Value);
				y = int.Parse(aTile.Element("y").Value);

				string b = aTile.Element("buildable").Value;
				buildable[y][x] = bool.Parse(b);
				walkable[y][x] = bool.Parse(aTile.Element("walkable").Value);
				height[y][x] = int.Parse(aTile.Element("height").Value);
			}

			throw new NotImplementedException();

			//Map map = new Map(hash, name, w, h, buildable, walkable, height, startingLocations, staticMinerals, staticGas);
			//return map;

		}
	}
}
